package com.cat.util.join;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.cat.util.join.consts.Consts;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.Objects;

/**
 * @program: eunomia
 * @description: join 的 Parameter 类
 * @author: wangchangquan/cat
 * @create: 2018-08-21-16:31
 **/
public class Parameter {

    public static final String DEFAULT_LEFT_PREFIX = "";
    public static final String DEFAULT_LEFT_PREFIX_SPLITOR = ".";

    public static final String PARAM_LEFT_PROP_EXP = "leftPropExp";
    public static final String PARAM_LEFT_RECIVE_PROP_EXP = "leftRecivePropExp";
    public static final String PARAM_RIGHT = "right";
    public static final String PARAM_RIGHT_METHOD = "rightMethod";
    public static final String PARAM_RIGHT_PROP_EXP = "rightPropExp";
    public static final String PARAM_CONVERTER = "converter";
    public static final String PARAM_RECURSION = "recursion";
    public static final String PARAM_NEXT_LEFT_PREFIX = "nextLeftPrefix";

    /**
     * 解析注解获取任务参数
     * @param leftPrefix 左边属性前缀 递归时加上路径深度
     * @param field 被解析注解的字段
     * @param context 上下文
     * @return
     */
    public static Map<String, Object> parseLeftOuter(String leftPrefix, Field field, Object context) {

        // 验证上下文
        if (!validateContext(context)) {
            return null;
        }

        // 创建返回参数对象
        Map<String, Object> param = CollUtil.newHashMap();

        // 获取参数访问
        boolean accessible = field.isAccessible();

        // 允许访问参数
        field.setAccessible(true);
        try {
            // 获取注解并验证有效性
            LeftOuter ol = field.getAnnotation(LeftOuter.class);
            if (Objects.isNull(ol)) {
                return null;
            }
            // 获取字段名称设置到接收属性上
            String name = field.getName();
            param.put(PARAM_LEFT_RECIVE_PROP_EXP, name);
            param.put(PARAM_NEXT_LEFT_PREFIX, name + DEFAULT_LEFT_PREFIX_SPLITOR);

            // 创建Converter转换对象
            Class<? extends Converter> converter = ol.converter();
            if (converter == AutoConverter.class || converter == Converter.class) {
                // 动态生成转换对象
                param.put(PARAM_CONVERTER, (Converter)(o -> {
                    Object d = null;
                    if (Objects.nonNull(o)) {
                        Class<?> aClass = field.getType();
                        if (o.getClass() != aClass) {
                            d = ReflectUtil.newInstance(aClass);
                            BeanUtil.copyProperties(o, d);
                        } else {
                            d = o;
                        }
                    }
                    return d;
                }));
            } else {
                // 使用自定义转换对象
                param.put(PARAM_CONVERTER, ReflectUtil.newInstance(converter));
            }

            // 获取其他参数
            param.put(PARAM_RIGHT, ReflectUtil.invoke(context, Consts.APPLICATION_CONTEXT_METHOD_GET_BEAN, ol.right()));
            param.put(PARAM_LEFT_PROP_EXP, StrUtil.format(ol.leftProp(), param));
            param.put(PARAM_RIGHT_METHOD, StrUtil.format(ol.rightMethod(), param));
            param.put(PARAM_RIGHT_PROP_EXP, StrUtil.format(ol.rightProp(), param));
            param.put(PARAM_RECURSION, ol.recursion());

            // 配置前缀
            param.put(PARAM_LEFT_RECIVE_PROP_EXP, leftPrefix + param.get(PARAM_LEFT_RECIVE_PROP_EXP));
            param.put(PARAM_NEXT_LEFT_PREFIX, leftPrefix + param.get(PARAM_NEXT_LEFT_PREFIX));
            param.put(PARAM_LEFT_PROP_EXP, leftPrefix + param.get(PARAM_LEFT_PROP_EXP));

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            field.setAccessible(accessible);
        }

        return param;
    }

    /**
     * 验证spring的上下文
     * @param context 的上下文
     * @return
     */
    private static boolean validateContext(Object context) {
        if (Objects.nonNull(context) && Objects.nonNull(ReflectUtil.getMethod(context.getClass(), Consts.APPLICATION_CONTEXT_METHOD_GET_BEAN, Class.class))) {
            return true;
        }
        return false;
    }
}
